/// @file unrootedtree.cc
/// @brief Methods associated with the unrooted tree structures

// *************************************************************************

// Copyright© 2010 Texas A&M University, 
//                 College Station, Texas
//                 Contact: R Crosby <rwc@cs.tamu.edu>
//
// Copyright© 2003, 2004 by BiRC -- Bioinformatics Research Center
//                 University of Aarhus, Denmark
//                 Contact: Thomas Mailund <mailund@birc.dk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// *************************************************************************

#include <cassert>

using namespace std;

#include "unrootedtree.hh"
#include "parser.hh"

void UnrootedTreeNode::connect(UnrootedTreeNode *utn) 
{
    add_neighbour(utn);
    utn->add_neighbour(this);
}

void UnrootedTreeLeaf::add_neighbour(UnrootedTreeNode *utn) 
{
    assert(_neighbour == 0);
    _neighbour = utn;
}

void UnrootedTreeLeaf::replace_neighbour(UnrootedTreeNode *original,
					 UnrootedTreeNode *replacement) 
{
    assert(_neighbour == original);
    _neighbour = replacement;
}

void UnrootedTreeLeaf::accept(UnrootedTreeVisitor *visitor)
{
    visitor->visit_leaf(this);
}


void UnrootedTreeInner::add_neighbour(UnrootedTreeNode *utn) 
{
    if (_neighbours.size() >= 3) throw Parser::DegreeError();
    _neighbours.push_back(utn);
}

void UnrootedTreeInner::replace_neighbour(UnrootedTreeNode *original,
					  UnrootedTreeNode *replacement) 
{
    for (size_t i = 0; i < _neighbours.size(); ++i)
    {
	if (_neighbours[i] == original)
	{
	    _neighbours[i] = replacement;
	    return;
	}
    }
    assert(false); // original wasn't in _neighbours
}

void UnrootedTreeInner::accept(UnrootedTreeVisitor *visitor)
{
    visitor->visit_inner(this);
}

void UnrootedTreeInner::build(UnrootedTree *ut, UnrootedTreeNode *prev) 
{
    if (_neighbours.size() == 2)
    {
	UnrootedTreeNode *next;
	if (prev == 0)
	{
	    prev = _neighbours[0];
	    next = _neighbours[1];
	    prev->replace_neighbour(this,next);
	    next->replace_neighbour(this,prev);
	    next->build(ut,prev);
	    prev->build(ut,next);
	    delete this;
	}
	else
	{
	    next = (_neighbours[0] == prev) ? _neighbours[1] : _neighbours[0];
	    prev->replace_neighbour(this,next);
	    next->replace_neighbour(this,prev);
	    next->build(ut,prev);
	    delete this;
	}
    }
    else if (_neighbours.size() != 3)
    {
	throw Parser::DegreeError();
    }
    else
    {
	assert(_neighbours.size() == 3);
	ut->nodes().push_back(this);
	for(unsigned int i = 0; i < _neighbours.size(); ++i)
	{
	    if(prev != _neighbours[i]) 
	    {
		_neighbours[i]->build(ut,this);
	    }
	}
    }
}

void UnrootedTreeLeaf::build(UnrootedTree *ut, UnrootedTreeNode *prev) 
{
    ut->leafs().push_back(this);
    ut->nodes().push_back(this);
    
    assert(_neighbour != 0);
    if(prev != _neighbour) 
    {
	_neighbour->build(ut, this);
    }
}

UnrootedTree::~UnrootedTree()
{
    while ( !_nodes.empty() ) {
	delete _nodes.back();
	_nodes.pop_back();
    }
}
